Skip to content

fix(onboard): recreate sandbox on model/provider drift for same-name …#1521

Open
ChunkyMonkey11 wants to merge 7 commits intoNVIDIA:mainfrom
ChunkyMonkey11:fix/1457-update-model-on-reonboard
Open

fix(onboard): recreate sandbox on model/provider drift for same-name …#1521
ChunkyMonkey11 wants to merge 7 commits intoNVIDIA:mainfrom
ChunkyMonkey11:fix/1457-update-model-on-reonboard

Conversation

@ChunkyMonkey11
Copy link
Copy Markdown
Contributor

@ChunkyMonkey11 ChunkyMonkey11 commented Apr 6, 2026

Summary

Fixes same-name re-onboard behavior where OpenClaw UI could keep showing a stale model/provider.
On onboarding with an existing ready sandbox, we now detect provider/model drift and avoid silent reuse by triggering recreate flow (with an interactive safety confirmation).

Related Issue

Fixes #1457

Changes

  • Added provider/model drift detection in onboarding sandbox-reuse path.
  • Added sandbox selection config read from sandbox via openshell sandbox download (/sandbox/.nemoclaw/config.json).
  • Added interactive confirmation prompt before destructive recreate when drift is detected.
  • Added explicit non-interactive recreate log path for drift.
  • Added tests in test/onboard.test.js covering:
    • drift detection/no silent reuse logic presence
    • confirmation guard behavior presence

Type of Change

  • Code change for a new feature, bug fix, or refactor.
  • Code change with doc updates.
  • Doc only. Prose changes without code sample modifications.
  • Doc only. Includes code sample changes.

Testing

  • npx prek run --all-files passes (or equivalently make check).
  • npm test passes.
  • make docs builds without warnings. (for doc-only changes)

Checklist

General

Code Changes

  • Formatters applied — npx prek run --all-files auto-fixes formatting (or make format for targeted runs).
  • Tests added or updated for new or changed behavior.
  • No secrets, API keys, or credentials committed.
  • Doc pages updated for any user-facing behavior changes (new commands, changed defaults, new features, bug fixes that contradict existing docs).

Doc Changes

  • Follows the style guide. Try running the update-docs agent skill to draft changes while complying with the style guide. For example, prompt your agent with "/update-docs catch up the docs for the new changes I made in this PR."
  • New pages include SPDX license header and frontmatter, if creating a new page.
  • Cross-references and links verified.

Signed-off-by: Revant Patel [email protected]

Summary by CodeRabbit

  • New Features

    • Onboarding detects inference provider/model drift and recreates sandboxes when needed
    • Interactive mode prompts for confirmation before destructive recreation
    • Non-interactive mode conditionally recreates when stored selection is readable; otherwise reuses by default
    • Added NEMOCLAW_RECREATE_SANDBOX env var to force recreation
  • Documentation

    • Updated onboard command docs with drift detection and mode-specific workflows
  • Tests

    • Added/updated tests covering drift detection and interactive confirmation flows

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds inference selection drift detection to onboarding: the CLI downloads an existing sandbox's selection config, compares provider/model to the requested values, and conditionally recreates the sandbox (interactive prompt or non-interactive logic). Also documents a NEMOCLAW_RECREATE_SANDBOX=1 env var.

Changes

Cohort / File(s) Summary
Documentation
docs/reference/commands.md
Documented sandbox recreation behavior when inference selection drifts, differences between interactive and non-interactive flows, and the NEMOCLAW_RECREATE_SANDBOX=1 environment variable.
Core Implementation
src/lib/onboard.ts
Added download-and-compare logic for existing sandbox config.json (provider/model), computed drift states (including unknown fallback), adjusted reuse vs recreate branching for interactive and non-interactive modes, distinct logging for drift-triggered recreation, and best-effort cleanup of temporary download dirs.
Tests
test/onboard.test.ts
Extended mocks for child_process/fs/path to simulate sandbox downloads and write config.json; added/updated tests for drift-detection behavior, interactive confirmation flows, and assertions verifying presence of selectionDrift handling and related log messages.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as NemoClaw CLI
    participant Sandbox as Existing Sandbox
    participant Config as Sandbox Config (config.json)
    participant UI as OpenClaw UI

    User->>CLI: nemoclaw onboard (same name, new selection)
    CLI->>Sandbox: request download of selection config
    Sandbox-->>Config: provide config.json (provider/model)
    Config-->>CLI: return provider/model or error
    CLI->>CLI: compare requested vs stored selection (drift detection)

    alt Drift detected
        alt Interactive
            CLI->>User: prompt to confirm delete+recreate
            User-->>CLI: confirm / abort
        else Non-interactive
            CLI->>CLI: auto-decide based on readable drift / unknown
        end
        CLI->>Sandbox: delete existing sandbox
        CLI->>Sandbox: create new sandbox with requested selection
        Sandbox-->>UI: reflect new inference configuration
        UI-->>User: show updated model/provider
    else No drift
        CLI->>Sandbox: reuse existing sandbox
        UI-->>User: show unchanged model/provider
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I dug through configs, sniffed the air,
Found models changed — a puzzling affair.
I nudged the sandbox, rebuilt with cheer,
Now OpenClaw shows the model clear.
Hop, rebuild, and all is right here. 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(onboard): recreate sandbox on model/provider drift for same-name...' clearly summarizes the main change—detecting provider/model drift during re-onboard and recreating the sandbox.
Linked Issues check ✅ Passed The PR implementation fully addresses issue #1457: detects model/provider drift when re-onboarding with the same sandbox name, prevents silent reuse of stale sandboxes, provides interactive confirmation, and ensures OpenClaw TUI reflects updated selection.
Out of Scope Changes check ✅ Passed All changes are directly scoped to drift detection and sandbox recreation: documentation of behavior, drift detection logic, interactive confirmation flow, and corresponding test coverage—no extraneous modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@ChunkyMonkey11
Copy link
Copy Markdown
Contributor Author

@coderabbitai please re-review

The branch head changed after I merged main into this PR branch.
No functional changes to the fix intent; requesting a fresh pass on the latest head commit.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

@ChunkyMonkey11 Sure! I'll kick off a fresh review on the latest head commit now.

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/onboard.test.js (1)

1977-1999: These drift tests grep source text instead of exercising the behavior.

They can miss runtime regressions and also fail on harmless helper/log renames. Please reuse the existing spawn-based harness patterns in this file to assert actual createSandbox() outcomes for provider-only drift, model drift, and unreadable config.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/onboard.test.js` around lines 1977 - 1999, Replace the static
source-text grep assertions with real integration tests that spawn the
onboarding binary and assert createSandbox() behavior: use the existing
spawn-based harness pattern already present in this test suite to run scenarios
that trigger provider-only drift, model drift, and unreadable config; invoke the
code paths that call getSelectionDrift and confirmRecreateForSelectionDrift,
then assert the process output/exit codes and that createSandbox was actually
recreated or left unchanged as expected (including interactive prompt behavior
for confirmRecreateForSelectionDrift), rather than matching log helper text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/lib/onboard.js`:
- Around line 924-967: readSandboxSelectionConfig can return null on
download/parse failures, but getSelectionDrift currently treats null as "no
drift"; change getSelectionDrift so that when
readSandboxSelectionConfig(sandboxName) returns null (unknown state) it does not
silently report changed: false — instead set changed: true to force
reconfiguration (or, if you prefer to surface unknown explicitly, set changed:
true and set providerChanged/modelChanged to null to indicate unknown rather
than false). Update the getSelectionDrift function to detect existing === null,
return changed: true, keep existingProvider/existingModel as null, and set
providerChanged/modelChanged to null (or true if you choose force-recreate
semantics) so callers can distinguish an actual mismatch from an
unreadable/unknown config.

---

Nitpick comments:
In `@test/onboard.test.js`:
- Around line 1977-1999: Replace the static source-text grep assertions with
real integration tests that spawn the onboarding binary and assert
createSandbox() behavior: use the existing spawn-based harness pattern already
present in this test suite to run scenarios that trigger provider-only drift,
model drift, and unreadable config; invoke the code paths that call
getSelectionDrift and confirmRecreateForSelectionDrift, then assert the process
output/exit codes and that createSandbox was actually recreated or left
unchanged as expected (including interactive prompt behavior for
confirmRecreateForSelectionDrift), rather than matching log helper text.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 57e767ad-4022-4702-a0e2-9a7d62fb935c

📥 Commits

Reviewing files that changed from the base of the PR and between 9a5dc38 and 613c75d.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard.test.js

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
bin/lib/onboard.js (1)

950-963: ⚠️ Potential issue | 🟠 Major

Unreadable selection config is still treated as “no drift.”

At Line 951, readSandboxSelectionConfig() can return null; at Line 962 this currently resolves to changed: false, which can silently reuse a sandbox when provider/model state is actually unknown.

🔧 Proposed fix
 function getSelectionDrift(sandboxName, requestedProvider, requestedModel) {
   const existing = readSandboxSelectionConfig(sandboxName);
+  if (existing === null) {
+    return {
+      changed: true,
+      providerChanged: false,
+      modelChanged: false,
+      existingProvider: null,
+      existingModel: null,
+      unknown: true,
+    };
+  }
   const existingProvider =
     existing && typeof existing.provider === "string" ? existing.provider : null;
   const existingModel = existing && typeof existing.model === "string" ? existing.model : null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/onboard.js` around lines 950 - 963, getSelectionDrift currently
treats a missing or unreadable selection config as "no drift"; change it so that
when readSandboxSelectionConfig(sandboxName) returns null/invalid, the function
treats the state as drifted. Specifically, in getSelectionDrift (and using the
existing variables existing, existingProvider, existingModel, providerChanged,
modelChanged), detect if existing is falsy or has non-string provider/model and
set the returned changed flag to true (and/or set providerChanged/modelChanged
appropriately) so we don't silently reuse a sandbox when the selection config is
unknown.
🧹 Nitpick comments (1)
test/onboard.test.js (1)

1977-1999: Prefer behavior-driven assertions over source-text regex for drift logic.

These checks can pass even if runtime drift handling regresses but strings remain. Consider adding one execution-path test that stubs sandbox download + prompt result and asserts reuse vs recreate/abort outcomes directly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/onboard.test.js` around lines 1977 - 1999, Replace the brittle
source-text regex tests with an execution-path test that stubs the runtime
behavior: import the onboard flow (the module that calls getSelectionDrift),
stub sandbox.download (or the sandbox object used by onboard.js) to simulate an
existing sandbox, stub getSelectionDrift to return changed=true and
changed=false cases, and stub the interactive prompt used by
confirmRecreateForSelectionDrift to return yes/no; then assert the actual
outcome (that the code calls the recreate path vs the reuse/abort path) by
spying on the functions that perform recreate (e.g., sandbox.recreate or the
recreate handler) and reuse (e.g., sandbox.connect) — exercise both changed=true
with prompt=confirm (expect recreate) and changed=true with prompt=abort (expect
abort/no recreate) and changed=false (expect reuse), so tests validate runtime
behavior rather than matching strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@bin/lib/onboard.js`:
- Around line 950-963: getSelectionDrift currently treats a missing or
unreadable selection config as "no drift"; change it so that when
readSandboxSelectionConfig(sandboxName) returns null/invalid, the function
treats the state as drifted. Specifically, in getSelectionDrift (and using the
existing variables existing, existingProvider, existingModel, providerChanged,
modelChanged), detect if existing is falsy or has non-string provider/model and
set the returned changed flag to true (and/or set providerChanged/modelChanged
appropriately) so we don't silently reuse a sandbox when the selection config is
unknown.

---

Nitpick comments:
In `@test/onboard.test.js`:
- Around line 1977-1999: Replace the brittle source-text regex tests with an
execution-path test that stubs the runtime behavior: import the onboard flow
(the module that calls getSelectionDrift), stub sandbox.download (or the sandbox
object used by onboard.js) to simulate an existing sandbox, stub
getSelectionDrift to return changed=true and changed=false cases, and stub the
interactive prompt used by confirmRecreateForSelectionDrift to return yes/no;
then assert the actual outcome (that the code calls the recreate path vs the
reuse/abort path) by spying on the functions that perform recreate (e.g.,
sandbox.recreate or the recreate handler) and reuse (e.g., sandbox.connect) —
exercise both changed=true with prompt=confirm (expect recreate) and
changed=true with prompt=abort (expect abort/no recreate) and changed=false
(expect reuse), so tests validate runtime behavior rather than matching strings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b1330725-b633-4790-bd7d-d83a1ef03843

📥 Commits

Reviewing files that changed from the base of the PR and between 9a5dc38 and 613c75d.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard.test.js

@ChunkyMonkey11
Copy link
Copy Markdown
Contributor Author

All good to go pasts the test cases.

@wscurran wscurran added Getting Started Use this label to identify setup, installation, or onboarding issues. priority: high Important issue that should be resolved in the next release NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). OpenShell Support for OpenShell, a safe, private runtime for autonomous AI agents fix labels Apr 6, 2026
@wscurran
Copy link
Copy Markdown
Contributor

wscurran commented Apr 6, 2026

✨ Thanks for submitting this fix, which proposes a way to detect provider/model drift during re-onboarding and prevent silent reuse of stale sandboxes.

Possibly related open issues:

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/onboard.test.js`:
- Around line 2039-2134: The test currently only asserts a recorded "'sandbox'
'delete' 'my-assistant'" command but doesn't verify the recreate step because
childProcess.spawn isn't recorded; update the in-test fixture (the script string
that requires createSandbox) to record spawn invocations (override
childProcess.spawn to push the command into the same commands array as
runner.run does) and then assert payload.commands contains a spawn/create entry
(e.g., command string includes "'sandbox' 'create' 'my-assistant'") in addition
to the existing delete assertion so the test ensures both delete and recreate
occur; reference the createSandbox invocation, the childProcess.spawn override,
and payload.commands when making the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 756d9712-383a-46fd-91cd-ccabdecdcea7

📥 Commits

Reviewing files that changed from the base of the PR and between a1c9d0f and 90ec84e.

📒 Files selected for processing (3)
  • bin/lib/onboard.js
  • docs/reference/commands.md
  • test/onboard.test.js
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/commands.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • bin/lib/onboard.js

@ChunkyMonkey11
Copy link
Copy Markdown
Contributor Author

All required checks pass; docstring coverage warning noted, can be handled in a follow-up cleanup PR if needed.

@ChunkyMonkey11
Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

✅ Actions performed

Reviews resumed.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
test/onboard.test.js (1)

2209-2218: ⚠️ Potential issue | 🟡 Minor

Test may pass spuriously when NEMOCLAW_NON_INTERACTIVE is set in CI.

The spawn env spreads process.env without explicitly clearing NEMOCLAW_NON_INTERACTIVE. If the test runner sets this variable (common in CI), the spawned script inherits it, skips the prompt entirely, and the test passes without exercising the interactive confirmation path.

🛡️ Proposed fix: explicitly clear the env variable
     const result = spawnSync(process.execPath, [scriptPath], {
       cwd: repoRoot,
       encoding: "utf-8",
       env: {
         ...process.env,
         HOME: tmpDir,
         PATH: `${fakeBin}:${process.env.PATH || ""}`,
+        NEMOCLAW_NON_INTERACTIVE: "",
       },
       timeout: 12000,
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/onboard.test.js` around lines 2209 - 2218, The spawned child inherits
process.env which can contain NEMOCLAW_NON_INTERACTIVE from CI, causing the
interactive branch to be skipped; modify the test to explicitly clear that
variable when building the env for spawnSync (change the env passed to spawnSync
for the invocation that uses
spawnSync/process.execPath/scriptPath/tmpDir/fakeBin to set
NEMOCLAW_NON_INTERACTIVE to undefined or remove it from the copied env before
adding HOME and PATH) so the child always runs in interactive mode for this
test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@test/onboard.test.js`:
- Around line 2209-2218: The spawned child inherits process.env which can
contain NEMOCLAW_NON_INTERACTIVE from CI, causing the interactive branch to be
skipped; modify the test to explicitly clear that variable when building the env
for spawnSync (change the env passed to spawnSync for the invocation that uses
spawnSync/process.execPath/scriptPath/tmpDir/fakeBin to set
NEMOCLAW_NON_INTERACTIVE to undefined or remove it from the copied env before
adding HOME and PATH) so the child always runs in interactive mode for this
test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0a3868aa-7938-4659-9a92-f9ebb157dd6c

📥 Commits

Reviewing files that changed from the base of the PR and between a1c9d0f and 98df39b.

📒 Files selected for processing (3)
  • bin/lib/onboard.js
  • docs/reference/commands.md
  • test/onboard.test.js
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/commands.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • bin/lib/onboard.js

@ChunkyMonkey11 ChunkyMonkey11 force-pushed the fix/1457-update-model-on-reonboard branch from 30b365f to 4f5eecf Compare April 9, 2026 00:49
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
test/onboard.test.js (1)

2784-2793: ⚠️ Potential issue | 🟡 Minor

Clear the recreate/non-interactive env flags for this interactive subprocess.

This case inherits process.env verbatim, unlike the nearby interactive tests. If the runner already has NEMOCLAW_NON_INTERACTIVE or NEMOCLAW_RECREATE_SANDBOX set, the confirmation path is skipped and this test no longer covers the decline flow.

🔧 Suggested fix
-    const result = spawnSync(process.execPath, [scriptPath], {
-      cwd: repoRoot,
-      encoding: "utf-8",
-      env: {
-        ...process.env,
-        HOME: tmpDir,
-        PATH: `${fakeBin}:${process.env.PATH || ""}`,
-      },
-      timeout: 12000,
-    });
+    const env = {
+      ...process.env,
+      HOME: tmpDir,
+      PATH: `${fakeBin}:${process.env.PATH || ""}`,
+    };
+    delete env.NEMOCLAW_NON_INTERACTIVE;
+    delete env.NEMOCLAW_RECREATE_SANDBOX;
+    const result = spawnSync(process.execPath, [scriptPath], {
+      cwd: repoRoot,
+      encoding: "utf-8",
+      env,
+      timeout: 12000,
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/onboard.test.js` around lines 2784 - 2793, The spawned subprocess in the
test (the spawnSync call that runs scriptPath with cwd repoRoot and env built
from ...process.env, HOME: tmpDir and PATH: `${fakeBin}:${process.env.PATH ||
""}`) unintentionally inherits NEMOCLAW_NON_INTERACTIVE and
NEMOCLAW_RECREATE_SANDBOX from the parent; explicitly clear those flags in the
env you pass to spawnSync (e.g., set NEMOCLAW_NON_INTERACTIVE and
NEMOCLAW_RECREATE_SANDBOX to undefined or an empty string in the env object) so
the interactive confirmation path is exercised and the decline flow is tested.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/onboard.test.js`:
- Around line 2700-2717: The test "detects provider/model drift and avoids
silent reuse" references payload.commands but never defines payload, causing a
ReferenceError; fix by creating or reusing a payload object with a commands
array before the assertion (e.g., initialize payload = { commands: [] } or
obtain the same payload used in the prior end-to-end case) so the assertion on
payload.commands.some(...) can run; update the test setup near the assertions
that check for "'sandbox' 'create'" so it uses the defined payload variable used
elsewhere in the test suite (or mock the command payload) before the final
assert.ok call.

---

Duplicate comments:
In `@test/onboard.test.js`:
- Around line 2784-2793: The spawned subprocess in the test (the spawnSync call
that runs scriptPath with cwd repoRoot and env built from ...process.env, HOME:
tmpDir and PATH: `${fakeBin}:${process.env.PATH || ""}`) unintentionally
inherits NEMOCLAW_NON_INTERACTIVE and NEMOCLAW_RECREATE_SANDBOX from the parent;
explicitly clear those flags in the env you pass to spawnSync (e.g., set
NEMOCLAW_NON_INTERACTIVE and NEMOCLAW_RECREATE_SANDBOX to undefined or an empty
string in the env object) so the interactive confirmation path is exercised and
the decline flow is tested.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ae5e18ab-9e02-4c3d-ac29-f026a004e2b8

📥 Commits

Reviewing files that changed from the base of the PR and between 30b365f and 4f5eecf.

📒 Files selected for processing (3)
  • bin/lib/onboard.js
  • docs/reference/commands.md
  • test/onboard.test.js
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/commands.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • bin/lib/onboard.js

Copy link
Copy Markdown
Contributor

@cv cv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Review — WARNING (3 issues)

The drift-detection approach is sound and the interactive confirmation is good. But three issues need fixing.

Required changes

1. Unknown config should not auto-destroy (HIGH):
When the config file can't be read (drift.unknown === true), non-interactive mode unconditionally returns true (recreate). A transient download failure or unreachable sandbox would destroy a perfectly good sandbox. Default to reuse when config is unreadable in non-interactive mode, or require NEMOCLAW_RECREATE_SANDBOX=1 explicitly.

2. Not-ready sandbox guard dropped (HIGH):
The old code had explicit handling for existingSandboxState !== "ready" — it prompted in interactive mode or exited in non-interactive mode. The new code falls straight through to delete-and-recreate with no prompt. This is a behavioral regression. Restore the not-ready guard.

3. Broken test — payload undefined (MEDIUM):
The "detects provider/model drift" test references payload.commands.some(...) but payload is never defined in that scope. Likely copy-paste from the "unreadable config" test. Will throw ReferenceError.

What's good

  • Interactive confirmation guard is correct
  • Config file only stores provider/model (no secrets)
  • Temp directory handling with finally cleanup is clean
  • stdio: ["ignore", ...] suppresses download output

@ChunkyMonkey11 ChunkyMonkey11 force-pushed the fix/1457-update-model-on-reonboard branch from 82be5fb to ffccec1 Compare April 9, 2026 20:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/onboard.ts (1)

2459-2498: ⚠️ Potential issue | 🟠 Major

Skip the drift recreate flow when the saved selection is unreadable.

getSelectionDrift() reports { changed: true, unknown: true } on download/parse failures. This branch only checks changed, so an unreadable config.json can send interactive users into the destructive recreate path even though no provider/model drift was actually confirmed. The same flag also drives the “apply model/provider change” recreate note below. Fall back to the normal reuse prompt when unknown is true.

Suggested fix
-        if (selectionDrift.changed) {
+        if (selectionDrift.changed && !selectionDrift.unknown) {
           const confirmed = await confirmRecreateForSelectionDrift(
             sandboxName,
             selectionDrift,
             provider,
             model,
@@
-    } else if (selectionDrift.changed) {
+    } else if (selectionDrift.changed && !selectionDrift.unknown) {
       note(`  Sandbox '${sandboxName}' exists — recreating to apply model/provider change.`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/onboard.ts` around lines 2459 - 2498, getSelectionDrift() can return
{changed:true, unknown:true} when config.json is unreadable; current logic
treats any changed as confirmed drift and may call
confirmRecreateForSelectionDrift/force recreate paths even though unknown should
fall back to reuse prompt. Update the branches that inspect selectionDrift (both
the interactive recreate prompt using confirmRecreateForSelectionDrift and the
later note that says "apply model/provider change") to first check
selectionDrift.unknown and, when true, treat it as not-confirmed drift (i.e.,
behave like changed === false): fallback to the normal reuse prompt flow
(calling upsertMessagingProviders/ensureDashboardForward and returning
sandboxName) instead of entering the destructive recreate path or emitting the
recreate note; keep confirmRecreateForSelectionDrift only for cases where
selectionDrift.changed is true and unknown is false.
🧹 Nitpick comments (1)
docs/reference/commands.md (1)

109-112: Minor passive voice construction.

Line 111 uses passive voice: "if the stored selection cannot be read". Consider active voice for consistency with the style guide.

✏️ Suggested rewrite
-In non-interactive mode, NemoClaw recreates automatically when the stored selection is readable and differs; if the stored selection cannot be read, NemoClaw reuses by default.
+In non-interactive mode, NemoClaw recreates automatically when the stored selection is readable and differs; if NemoClaw cannot read the stored selection, it reuses by default.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/reference/commands.md` around lines 109 - 112, Change the passive clause
"if the stored selection cannot be read" to active voice for consistency; update
the sentence in docs/reference/commands.md to something like "if NemoClaw cannot
read the stored selection, NemoClaw reuses by default" (ensure the phrase still
flows with the surrounding sentences mentioning interactive mode and
NEMOCLAW_RECREATE_SANDBOX).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/lib/onboard.ts`:
- Around line 2459-2498: getSelectionDrift() can return {changed:true,
unknown:true} when config.json is unreadable; current logic treats any changed
as confirmed drift and may call confirmRecreateForSelectionDrift/force recreate
paths even though unknown should fall back to reuse prompt. Update the branches
that inspect selectionDrift (both the interactive recreate prompt using
confirmRecreateForSelectionDrift and the later note that says "apply
model/provider change") to first check selectionDrift.unknown and, when true,
treat it as not-confirmed drift (i.e., behave like changed === false): fallback
to the normal reuse prompt flow (calling
upsertMessagingProviders/ensureDashboardForward and returning sandboxName)
instead of entering the destructive recreate path or emitting the recreate note;
keep confirmRecreateForSelectionDrift only for cases where
selectionDrift.changed is true and unknown is false.

---

Nitpick comments:
In `@docs/reference/commands.md`:
- Around line 109-112: Change the passive clause "if the stored selection cannot
be read" to active voice for consistency; update the sentence in
docs/reference/commands.md to something like "if NemoClaw cannot read the stored
selection, NemoClaw reuses by default" (ensure the phrase still flows with the
surrounding sentences mentioning interactive mode and
NEMOCLAW_RECREATE_SANDBOX).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0fa282c6-e4d7-441e-929d-715cab9783b4

📥 Commits

Reviewing files that changed from the base of the PR and between 82be5fb and ffccec1.

📒 Files selected for processing (3)
  • docs/reference/commands.md
  • src/lib/onboard.ts
  • test/onboard.test.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/lib/onboard.ts (1)

811-817: Consider explicitly setting unknown: false for consistency.

The return object lacks the unknown property when drift comparison succeeds. While the check at line 2425 (!selectionDrift.unknown) works correctly because !undefined === true, explicitly setting unknown: false would make the type shape consistent and the intent clearer.

Suggested change
   return {
     changed: providerChanged || modelChanged,
     providerChanged,
     modelChanged,
     existingProvider,
     existingModel,
+    unknown: false,
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/onboard.ts` around lines 811 - 817, The returned drift object should
include unknown: false explicitly so its shape is consistent; update the return
in the function that currently returns { changed: providerChanged ||
modelChanged, providerChanged, modelChanged, existingProvider, existingModel }
to also include unknown: false, ensuring code that checks
!selectionDrift.unknown (referenced elsewhere) sees an explicit boolean instead
of undefined.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/onboard.ts`:
- Around line 811-817: The returned drift object should include unknown: false
explicitly so its shape is consistent; update the return in the function that
currently returns { changed: providerChanged || modelChanged, providerChanged,
modelChanged, existingProvider, existingModel } to also include unknown: false,
ensuring code that checks !selectionDrift.unknown (referenced elsewhere) sees an
explicit boolean instead of undefined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 853a1699-49d4-4866-9512-c348750d1734

📥 Commits

Reviewing files that changed from the base of the PR and between ffccec1 and ed52a5d.

📒 Files selected for processing (3)
  • docs/reference/commands.md
  • src/lib/onboard.ts
  • test/onboard.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/commands.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/onboard.test.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Getting Started Use this label to identify setup, installation, or onboarding issues. NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). OpenShell Support for OpenShell, a safe, private runtime for autonomous AI agents priority: high Important issue that should be resolved in the next release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NemoClaw][All Platform] NemoClaw onboarded with the same name shows the old model name in OpenClaw TUI

3 participants